﻿/// <reference path="_namespace.js" />
/// <reference path="/ajax.drawing/Colour.js"/>
/// <reference path="/ajax/math/Vector3.js" />
/// <reference path="/ajax/math/Matrix.js" />
/// <reference path="/ajax.drawing/Face.js" />
/// <reference path="/ajax/drawing/LightTypes.js" />
/// <reference path="/ajax/drawing/Light.js" />



/*****************************************************************************
Ajaxna library class definition. 
Author: krobinson 11/10/2008 12:35:48 AM.
Class:  ajax.xna.framework.graphics.RenderFace
*****************************************************************************/

$addNamespace("ajax.xna.framework.graphics");
//-----------------------------------------------------------------------------

ajax.xna.framework.graphics.RenderFace = function(face, cameraNormal, cameraVB, screenVerts, projectionMatrix, zBias)
{///<param name="face" type="ajax.drawing.Face">The original face, in model space.</param>
    ///<param name="cameraNormal" type="ajax.math.Vector3">The normal of the face, transformed into Camera Space (World * View).</param>
    ///<param name="cameraVB" type="Array" elementType="ajax.math.Vector3">The vertex buffer array, transformed into Camera Space (World * View).</param>
    ///<param name="screenVerts" type="Array" elementType="ajax.math.Vector3">The vertex buffer array, Projected into Screen Space coordinates (World * View * Projection)</param>
    ///<param name="projectionMatrix" type="ajax.math.Matrix">The current projection matrix. This may be used to re-project the camera space verts to the screen if this renderface is ever split into multiple faces (near Z clipping).</param>
    ///<param name="zBias" type="float">The Z Bias to apply when rendering this face. Pass Zero for no bias.</param>
    ///<field name="normal" type="ajax.math.Vector3">The camera space normal of this face.</field>
    if (face)
    {
        this.color = face.getColor();
        this.cameraSpaceVertA = cameraVB[face.v1];
        this.cameraSpaceVertB = cameraVB[face.v2];
        this.cameraSpaceVertC = cameraVB[face.v3];
        this.screenSpaceVerts = new Array(3);
        this.screenSpaceVerts[0] = screenVerts[face.v1];
        this.screenSpaceVerts[1] = screenVerts[face.v2];
        this.screenSpaceVerts[2] = screenVerts[face.v3];
        this.normal = cameraNormal;
    }
    this.averageZ = 0;
    this.projectionMatrix = projectionMatrix;
    this.zBias = zBias;
    this.lights = [];
}
$extend("ajax.xna.framework.graphics.RenderFace", "");

// Public members
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.prototype.lights = [];
ajax.xna.framework.graphics.RenderFace.prototype.color = null; // ajax.drawing.Colour.prototype;
ajax.xna.framework.graphics.RenderFace.prototype.cameraSpaceVertA = null; // ajax.math.Vector3.prototype;
ajax.xna.framework.graphics.RenderFace.prototype.cameraSpaceVertB = null; // ajax.math.Vector3.prototype;
ajax.xna.framework.graphics.RenderFace.prototype.cameraSpaceVertC = null; // ajax.math.Vector3.prototype;
ajax.xna.framework.graphics.RenderFace.prototype.screenSpaceVerts = [];
ajax.xna.framework.graphics.RenderFace.prototype.normal = null; // ajax.math.Vector3.prototype;
ajax.xna.framework.graphics.RenderFace.prototype.averageZ = 0;
ajax.xna.framework.graphics.RenderFace.prototype.zBias = 0;
ajax.xna.framework.graphics.RenderFace.prototype.cullMode = 0;
ajax.xna.framework.graphics.RenderFace.prototype.fillMode = 0;
ajax.xna.framework.graphics.RenderFace.prototype.lighting = false;
ajax.xna.framework.graphics.RenderFace.prototype.projectionMatrix = null; // ajax.math.Matrix.prototype;


// Statics
//-----------------------------------------------------------------------------
//Static
ajax.xna.framework.graphics.RenderFace.computeColor = function(renderFace, device)
{///<param name="renderFace" type="ajax.drawing.RenderFace"/>
    ///<param name="device" type="ajax.xna.framework.graphics.GraphicsDevice"/>

    //device = ajax.xna.framework.graphics.GraphicsDevice.cast(device);

    // If lighting is off then there is nothing to do
    //if (!device.renderState.lighting) // Each face now holds the data of if it is affected by lighting or not.    
    if (!renderFace.lighting)
        return renderFace.color;


    // If lighting is ON but no lights are enabled, return black
    if (renderFace.lights.length < 1 || renderFace.lights[0].enabled == false)
        return new ajax.drawing.Colour(0, 0, 0, renderFace.color.a);
    /*
    if (device.lights.length < 1 || device.lights[0].enabled == false)
    return new ajax.drawing.Colour(0, 0, 0);
    */
    // Will hold the final light intensity for each light (Cannot be > 1)
    // We calc this by adding how much light intensity each ADDS to the face    
    var rIntensity = 0; var gIntensity = 0; var bIntensity = 0;

    //for (var i = 0; i < device.lights.length; i++)
    for (var i = 0; i < renderFace.lights.length; i++)
    {
        var light = ajax.drawing.Light.cast(renderFace.lights[i]);
        //var light = ajax.drawing.Light.cast(device.lights[i]);

        // Break when we get to a light that is not enabled.
        if (!light.enabled)
            break;

        var lightDirectionTransformedNormalised = new ajax.math.Vector3(0, 0, 0);

        switch (light.type)
        {
            case ajax.drawing.LightTypes.Directional:
                lightDirectionTransformedNormalised = ajax.math.Matrix.transformNormal(device.transform.get_view(), light.direction);
                break;
            case ajax.drawing.LightTypes.Omni:

                var lightPositionTransformed = ajax.math.Matrix.transformVertex(device.transform.get_view(), light.position);

                lightDirectionTransformedNormalised = ajax.math.Vector3.normalize(ajax.math.Vector3.subtract(renderFace.cameraSpaceVertA, lightPositionTransformed));

                break;
            default:
                break;

        }

        // Get the Cosign of the angle between the light direction and the face normal
        var cosAngle = ajax.math.Vector3.dot(renderFace.normal, lightDirectionTransformedNormalised);
        if (cosAngle > 0)
        {
            // Facing away so apply only ambient lighting            
            rIntensity += light.getRAmbientIntensity();
            gIntensity += light.getGAmbientIntensity();
            bIntensity += light.getBAmbientIntensity();

        }
        else
        {
            rIntensity += (-cosAngle * light.getRDiffuseIntensity()) + light.getRAmbientIntensity();
            gIntensity += (-cosAngle * light.getGDiffuseIntensity()) + light.getGAmbientIntensity();
            bIntensity += (-cosAngle * light.getBDiffuseIntensity()) + light.getBAmbientIntensity();

        }
    }

    var rVal = parseInt((rIntensity * parseFloat(renderFace.color.r)));
    var gVal = parseInt((gIntensity * parseFloat(renderFace.color.g)));
    var bVal = parseInt((bIntensity * parseFloat(renderFace.color.b)));

    rVal = Math.max(Math.min(rVal, 255), 0);
    gVal = Math.max(Math.min(gVal, 255), 0);
    bVal = Math.max(Math.min(bVal, 255), 0);

    return new ajax.drawing.Colour(rVal, gVal, bVal, renderFace.color.a);
}

//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.cast = function(obj)
{///<returns type="ajax.xna.framework.graphics.RenderFace"/>
    return obj;
}


// Public Methods
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.prototype.computeColor = function(device)
{///<returns type="ajax.drawing.Colour">Computes the color of this face based on the normal (cameraSpace) and the lights active for that face.</returns>    
    return ajax.xna.framework.graphics.RenderFace.computeColor(this, device);
}

//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.prototype.reComputeCameraToScreen = function(viewport)
{///<param name="viewport" type="ajax.xna.framework.graphics.Viewport"/>   
    viewport = ajax.xna.framework.graphics.Viewport.cast(viewport);
    var cameraVerts = new Array(3);
    cameraVerts[0] = this.cameraSpaceVertA;
    cameraVerts[1] = this.cameraSpaceVertB;
    cameraVerts[2] = this.cameraSpaceVertC;

    this.screenSpaceVerts = viewport.projectArray(cameraVerts, this.projectionMatrix);
}

//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.prototype.computeAverageZ = function()
{
    this.averageZ = 0.33333 * (
		this.cameraSpaceVertA.z +
		this.cameraSpaceVertB.z +
		this.cameraSpaceVertC.z) + this.zBias;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.prototype.setAverageZ = function(value)
{///<summary>Used only by internal rendering methods. Clients should use calculateAverageZ() method.</summary>
    this.averageZ = value;
}

// Ctor Overrides...
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.RenderFace.create = function(camSpaceVertA, camSpaceVertB, camSpaceVertC, colour, camSpaceNormal, viewport, projectionMatrix)
{///<summary>Factory creator - an Alternative "constructor"</summary>
    ///<returns type="ajax.drawing.RenderFace"/>
    ///<param name="camSpaceVertA" type="ajax.math.Vector3"/>
    ///<param name="camSpaceVertB" type="ajax.math.Vector3"/>
    ///<param name="camSpaceVertC" type="ajax.math.Vector3"/>
    ///<param name="colour" type="ajax.drawing.Colour"/>
    ///<param name="camSpaceNormal" type="ajax.math.Vector3"/>
    ///<param name="viewport" type="ajax.xna.framework.graphics.Viewport"/>
    ///<param name="projectionMatrix" type="ajax.math.Matrix">The current projection matrix. This is used to project the camera space verts to the screen.</param>

    var ret = new ajax.xna.framework.graphics.RenderFace(); // no args
    ret.color = colour;
    // Set the camera space verts
    ret.cameraSpaceVertA = camSpaceVertA;
    ret.cameraSpaceVertB = camSpaceVertB;
    ret.cameraSpaceVertC = camSpaceVertC;
    ret.projectionMatrix = projectionMatrix;
    // Compute the screen verts from the camera space verts
    ret.reComputeCameraToScreen(viewport);
    ret.normal = camSpaceNormal;

    return ret;

}